/**
 * Snippet from http://snippets.dzone.com .
 *
 * Licensed under the WTFPL (http://sam.zoy.org/wtfpl/).
 */
package de.sendorian.util.io;

import java.io.File;
import java.io.FileFilter;
import java.util.Arrays;
import java.util.Iterator;

import de.sendorian.util.FlatteningIterator;

/**
 * Iterates over all non-directory files contained in some subdirectory of the
 * current one.
 * 
 * @author david
 */
public class RecursiveFileListIterator implements Iterator<File> {
	private final FlatteningIterator flatteningIterator;

	private static FileFilter DUMMY_FILTER = new FileFilter() {
		@Override
		public boolean accept(File pathname) {
			return true;
		}
	};

	public void remove() {
	}

	public RecursiveFileListIterator(File file, FileFilter filter) {
		flatteningIterator = new FlatteningIterator(new FileIterator(file,
				filter));
	}

	public RecursiveFileListIterator(File file) {
		this(file, DUMMY_FILTER);
	}

	public boolean hasNext() {
		return flatteningIterator.hasNext();
	}

	public File next() {
		return (File) flatteningIterator.next();
	}

	/**
	 * Iterator to iterate over all the files contained in a directory. It
	 * returns a File object for non directories or a new FileIterator obejct
	 * for directories.
	 */
	private static class FileIterator implements Iterator<Object> {
		private final Iterator<File> files;

		private final FileFilter filter;

		FileIterator(File file, FileFilter passedFilter) {
			this.filter = new DirectoryPassingFileFilter(passedFilter);
			files = Arrays.asList(file.listFiles(this.filter)).iterator();
		}

		public void remove() {
		}

		public Object next() {
			File next = files.next();

			if (next.isDirectory()) {
				return new FileIterator(next, filter);
			} else {
				return next;
			}
		}

		public boolean hasNext() {
			return files.hasNext();
		}
	}

	/**
	 * Filter that lets all directories pass in addition to a given FileFilter.
	 * 
	 * @author sendorian
	 * 
	 */
	private static final class DirectoryPassingFileFilter implements FileFilter {

		private final FileFilter filter;

		DirectoryPassingFileFilter(FileFilter filter) {
			this.filter = filter;
		}

		@Override
		public boolean accept(File pathname) {
			return pathname != null
					&& (pathname.isDirectory() || filter.accept(pathname));
		}
	}
}
